home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Games Collection 1
/
software vault.zip
/
software vault
/
CDR10
/
YICN23.ZIP
/
SOURCE
/
PLAY_CMF.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1993-02-14
|
8KB
|
365 lines
/*
* Copyrighted as an unpublished work.
* (c) Copyright 1991 Brian Smith
* All rights reserved.
*
* Read the LICENSE file for details on distribution and use.
*
*/
#include <stdio.h>
#include <fcntl.h>
#include <dos.h>
#include <alloc.h>
#include <io.h>
#include <string.h>
#include <stdlib.h>
#include "sb.h"
#define TRUE 1
#define FALSE 0
#define lobyte(X) (((unsigned char *)&X)[0])
#define hibyte(X) (((unsigned char *)&X)[1])
/* Globals */
int fm_herz; /* clock ticks per second */
int tempo; /* clock ticks per quarter note */
int fm_fd;
int note_on[22];
char **instrument_table;
int note_table[12] = {
343,
363,
385,
408,
432,
458,
485,
514,
544,
577,
611,
647
};
/* check for "CTMF" in first four bytes of file */
int verify_cmf(int fd)
{
char idbuf[5];
/* get id */
lseek(fd, 0, SEEK_SET);
if (read(fd, idbuf, 4) != 4)
return(FALSE);
/* compare to standard id */
idbuf[4] = (char)0;
if (strcmp(idbuf, "CTMF") != 0)
return(FALSE);
return(TRUE);
}
int get_instruments(int fd)
{
int offset;
int num_instruments;
int i;
int rc;
int fnum, block, note;
unsigned char tmp_byte;
/* get offset of instrument block */
offset = 0;
lseek(fd, 0x06, SEEK_SET);
read(fd, &tmp_byte, 1);
lobyte(offset) = tmp_byte;
read(fd, &tmp_byte, 1);
hibyte(offset) = tmp_byte;
/* get number of instruments */
num_instruments = 0;
lseek(fd, 0x24, SEEK_SET);
read(fd, &tmp_byte, 1);
lobyte(num_instruments) = tmp_byte;
read(fd, &tmp_byte, 1);
hibyte(num_instruments) = tmp_byte;
/* allocate space */
instrument_table = (char **)malloc(sizeof(int *) * num_instruments);
/* read each instrument */
lseek(fd, (long)offset, SEEK_SET);
for (i=0; i< num_instruments; i++)
{
/* allocate space */
instrument_table[i] = (char *)malloc(16);
/* set instrument characteristics */
read(fd, instrument_table[i], 16);
}
return(0);
}
/*
* get and set timing parameters
*/
int set_timing(int fd)
{
unsigned char tmp_byte;
/* get tempo */
tempo = 0;
lseek(fd, 0x0C, SEEK_SET);
read(fd, &tmp_byte, 1);
tempo = (unsigned int)tmp_byte;
read(fd, &tmp_byte, 1);
tempo += (unsigned int)tmp_byte << 8;
/* get herz of timing clock */
fm_herz = 0;
lseek(fd, 0x0C, SEEK_SET);
read(fd, &tmp_byte, 1);
fm_herz = (unsigned int)tmp_byte;
read(fd, &tmp_byte, 1);
fm_herz += (unsigned int)tmp_byte << 8;
return(0);
}
/*
* read a variable length scalar in MIDI format
*/
int ReadVarLen(int fd)
{
int value;
unsigned char tmp_byte;
if (read(fd, &tmp_byte, 1) == 0)
return(-1);
value = (int)tmp_byte;
if (tmp_byte & 0x80)
{
value &= 0x7F;
do
{
if (read(fd, &tmp_byte, 1) == 0)
return(-1);
value = (value << 7) + (tmp_byte & 0x7F);
} while (tmp_byte & 0x80);
}
return(value);
}
/*
* load an instrument from the instrument table into the SoundBlaster
*/
int load_instrument(int channel, int instrument)
{
int rc;
/* error check! */
if ((channel <0) || (channel >= 9))
return 0;
/* abort instrument if being loaded */
if (note_on[channel])
Sb_FM_Key_Off(channel);
/* set instrument characteristics */
Sb_FM_Set_Voice(channel,instrument_table[instrument]);
return(0);
}
/*
* process a midi event
*/
int process_event(int fd)
{
int rc, channel;
unsigned char tmp_byte;
static int status = -1;
/* get status byte */
read(fd, &tmp_byte, 1);
if (tmp_byte & 0x80)
{
status = (unsigned int)tmp_byte;
}
else
{
/* running status, so back up one */
if (status == -1)
{
printf("ERROR in cmf file. Running status at beginning of file\n");
exit(-1);
}
lseek(fd, -1, SEEK_CUR);
}
/* switch different events */
switch (status & 0xF0)
{
case 0x80:
/* turn note off */
channel = status & 0x0f;
Sb_FM_Key_Off(channel);
note_on[channel] = 0;
/* waste two bytes */
read(fd, &tmp_byte, 1);
read(fd, &tmp_byte, 1);
break;
case 0x90:
/* get note */
read(fd, &tmp_byte, 1);
/* determine note */
/* turn note on */
channel = status & 0x0f;
if (note_on[channel])
Sb_FM_Key_Off(channel);
note_on[channel] = 1;
Sb_FM_Key_On(channel,note_table[tmp_byte % 12],(tmp_byte/12) & 7);
/* waste a bytes */
read(fd, &tmp_byte, 1);
break;
case 0xA0:
printf("polyphonic key pressure: not handled\n");
/* waste two bytes */
read(fd, &tmp_byte, 1);
read(fd, &tmp_byte, 1);
break;
case 0xB0:
printf("control change: not handled\n");
/* waste two bytes */
read(fd, &tmp_byte, 1);
read(fd, &tmp_byte, 1);
break;
case 0xC0:
/* change the instrument on a channel */
read(fd, &tmp_byte, 1);
load_instrument(status&0x0F, tmp_byte & 0x0F);
break;
case 0xD0:
printf("Channel Pressure: not handled\n");
/* waste a byte */
read(fd, &tmp_byte, 1);
break;
case 0xE0:
printf("Pitch Wheel Change: not handled\n");
/* waste two bytes */
read(fd, &tmp_byte, 1);
read(fd, &tmp_byte, 1);
break;
case 0xF0:
printf("System Exclusive: not handled\n");
/* waste two bytes */
read(fd, &tmp_byte, 1);
read(fd, &tmp_byte, 1);
break;
default:
printf("internal program error\n");
/* waste two bytes */
read(fd, &tmp_byte, 1);
read(fd, &tmp_byte, 1);
break;
}
return(0);
}
/*
* seek to the midi stream and handle midi events for the song
*/
int play_song(int fd)
{
int offset;
unsigned char tmp_byte;
int delta;
/* get offset of music stream */
lseek(fd, 8, SEEK_SET);
read(fd, &tmp_byte, 1);
offset = (unsigned int)tmp_byte;
read(fd, &tmp_byte, 1);
offset += (unsigned int)tmp_byte << 8;
lseek(fd, offset, SEEK_SET);
/* process till EOF */
while(1)
{
/* get delta time */
delta = ReadVarLen(fd);
if (delta == -1)
break;
/* wait delta */
if (delta > 0)
delay((double)delta/(double)fm_herz * 1000);
/* process midi event */
process_event(fd);
}
return(0);
}
int main(int argc, char **argv)
{
int cmf_fd;
if (argc != 2)
{
printf("usage: %s <cmf file>\n", argv[0]);
exit(-1);
}
/* open cmf file */
cmf_fd = open(argv[1], O_RDONLY);
if (cmf_fd == -1)
{
printf("usage: %s <cmf file>\n", argv[0]);
exit(-1);
}
/* verify that file is a cmf file */
if (!verify_cmf(cmf_fd))
{
printf("file was not a cmf file\n");
printf("usage: %s <cmf file>\n", argv[0]);
exit(-1);
}
/* read and set instruments from cmf file */
get_instruments(cmf_fd);
/* get timing */
set_timing(cmf_fd);
/* open soundblaster fm chips */
Sb_FM_Reset();
/* play song */
play_song(cmf_fd);
return(0);
}